#include "glwidget.h"

/*
 int flag values
        0: select object in scene
        1: select translate axis
        2: select rotate axis
        3: select scale axis
        4: select one point under edit mesh mode
        5: select one face under eidt mesh mode
 */
void GLWidget::processHits(GLint hits, GLuint buffer[], int flag)
{
    GLuint *ptr;
    GLint ptrNames;
    ptrNames = -1;
    GLuint minZ = 0xffffffff;
    ptr = (GLuint *) buffer;

    if (flag == 0)
    {
        selectObject = -1;
    }
    else if (flag == 1)
    {
        selectTranslateAxis = -1;
    }
    else if (flag == 2)
    {
        selectRotateAxis = -1;
    }
    else if(flag == 3)
    {
        selectScaleAxis = -1;
    }
    else if(flag == 4)
    {
        if (selectObject==0)
        {
            if(glandular->isSurfaceConstructed())
            {
                glandular->resetSelectedPoint();
            }
        }
        else if (selectObject>1)
        {
            objects[selectObject-2]->resetSelectedPoint();
        }
    }
    else if(flag == 5)
    {
        if (selectObject==0)
        {
            if(glandular->isSurfaceConstructed())
            {
                glandular->resetSelectedFace();
            }
        }
        else if (selectObject>1)
        {
            objects[selectObject-2]->resetSelectedFace();
        }
    }

    //printf("hits = %d\n", hits);
    for (int i = 0; i < hits; i++)
    {
        ptr++;

        /* Find the nearest control point in all selected points */
        if (*ptr < minZ)
        {
            minZ = *ptr;
            ptrNames = *(ptr+2);
        }
        ptr = ptr+3;
    }
    if (hits == 0 && (flag == 0 || flag == 6))
    {
        selectObject = -1;
        secondSelectObject = -1;
    }
    if (ptrNames >= 0)
    {
        if (flag==0)
        {
            selectObject = ptrNames;
            secondSelectObject = -1;
        }
        else if (flag==1)
        {
            selectTranslateAxis = ptrNames;
        }
        else if (flag==2)
        {
            selectRotateAxis = ptrNames;
        }
        else if (flag==3)
        {
            selectScaleAxis = ptrNames;
        }
        else if (flag==4)
        {
            if (selectObject==0)
            {
                if(glandular->isSurfaceConstructed())
                {
                    glandular->setPointSelect(ptrNames);
                }
            }
            else if (selectObject>1)
            {
                objects[selectObject-2]->setPointSelect(ptrNames);
            }
        }
        else if (flag==5) {
            if (selectObject==0)
            {
                glandular->setFaceSelect(ptrNames);
            }
            else if (selectObject>1)
            {
                objects[selectObject-2]->setFaceSelect(ptrNames);
            }
        }
        else if (flag==6)
        {
            secondSelectObject = -1;
            if (selectObject == -1)
            {
                selectObject = ptrNames;
            }
            if (selectObject != ptrNames)
            {
                secondSelectObject = ptrNames;
            }
            if (selectObject == secondSelectObject)
            {
                secondSelectObject = -1;
            }
        }
    }
}

void GLWidget::pick(int x, int y)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(BUFSIZE, selectBuf);
    (void) glRenderMode(GL_SELECT);	/* Enter select mode */
    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble) x,
                  (GLdouble) (viewport[3] - y),
                  10.0, 10.0,
                  viewport
                  );
    if (viewMode == CELL_PERSPECTIVE)
    {
        gluPerspective(45,(double)windowWidth/(double)windowHeight,0.01,1000);
    }
    else if (viewMode == CELL_TOP)
    {
        glOrtho(-topR*sin(pi/8), topR*sin(pi/8),
                -topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }
    else if (viewMode == CELL_FRONT)
    {
        glOrtho(-frontR*sin(pi/8), frontR*sin(pi/8),
                -frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }
    else if (viewMode == CELL_RIGHT)
    {
        glOrtho(-rightR*sin(pi/8), rightR*sin(pi/8),
                -rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (viewMode == CELL_PERSPECTIVE)
    {
        glTranslatef(offsetX,-offsetY,0);
        gluLookAt(R*cos(Phi)*cos(Theta), R*sin(Theta), R*sin(Phi)*cos(Theta),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    else if (viewMode == CELL_TOP)
    {
        glTranslatef(topOffsetX,-topOffsetY,0);
        gluLookAt(topR*cos(0.0)*cos(pi/2), topR*sin(pi/2), topR*sin(0.0)*cos(pi/2),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    else if (viewMode == CELL_FRONT)
    {
        glTranslatef(frontOffsetX,-frontOffsetY,0);
        gluLookAt(frontR*cos(0.0)*cos(0.0), frontR*sin(0.0), frontR*sin(0.0)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    else if (viewMode == CELL_RIGHT)
    {
        glTranslatef(rightOffsetX,-rightOffsetY,0);
        gluLookAt(rightR*cos(pi/2)*cos(0.0), rightR*sin(0.0), rightR*sin(pi/2)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    drawGlandular();
    //plane->DrawPlane(1, selectObject);

    glFlush();
    hits = glRenderMode(GL_RENDER);
    if (keyValue == CELL_KEYRELEASE)
    {
        processHits(hits, selectBuf, 0);
    }
    else if (keyValue == CELL_SHIFT)
    {
        processHits(hits, selectBuf, 6);
    }
    else
    {
        processHits(hits, selectBuf, 0);
    }

    resizeGL(windowWidth, windowHeight);
}

void GLWidget::pickInFourView(int x, int y)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    int halfWidth = windowWidth/2;
    int halfHeight = windowHeight/2;
    if (x<=halfWidth-CELL_FOURVIEWMARGIN && y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //top
        glViewport(0, halfHeight+CELL_FOURVIEWMARGIN,
                   halfWidth-CELL_FOURVIEWMARGIN, halfHeight-CELL_FOURVIEWMARGIN);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //perspective
        glViewport(halfWidth+CELL_FOURVIEWMARGIN, halfHeight+CELL_FOURVIEWMARGIN,
                   halfWidth-CELL_FOURVIEWMARGIN, halfHeight-CELL_FOURVIEWMARGIN);
    } else if (x<=halfWidth-CELL_FOURVIEWMARGIN &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //front
        glViewport(0, 0, halfWidth-CELL_FOURVIEWMARGIN, halfHeight-CELL_FOURVIEWMARGIN);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //right
        glViewport(halfWidth+CELL_FOURVIEWMARGIN, 0,
                   halfWidth-CELL_FOURVIEWMARGIN, halfHeight-CELL_FOURVIEWMARGIN);
    } else {
        return;
    }
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(BUFSIZE, selectBuf);
    (void) glRenderMode(GL_SELECT);	/* Enter select mode */
    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble) x,
                  (GLdouble) windowHeight-y,
                  10.0, 10.0,
                  viewport
                  );
    if (x<=halfWidth-CELL_FOURVIEWMARGIN && y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //top
        glOrtho(-topR*sin(pi/8), topR*sin(pi/8),
                -topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //perspective
        gluPerspective(45,(double)windowWidth/(double)windowHeight,0.01,1000);
    } else if (x<=halfWidth-CELL_FOURVIEWMARGIN &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //front
        glOrtho(-frontR*sin(pi/8), frontR*sin(pi/8),
                -frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //right
        glOrtho(-rightR*sin(pi/8), rightR*sin(pi/8),
                -rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (x<=halfWidth-CELL_FOURVIEWMARGIN && y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //top
        glTranslatef(topOffsetX,-topOffsetY,0);
        gluLookAt(topR*cos(0.0)*cos(pi/2), topR*sin(pi/2), topR*sin(0.0)*cos(pi/2),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y<=halfHeight-CELL_FOURVIEWMARGIN) {
        //pespective
        glTranslatef(offsetX,-offsetY,0);
        gluLookAt(R*cos(Phi)*cos(Theta), R*sin(Theta), R*sin(Phi)*cos(Theta),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (x<=halfWidth-CELL_FOURVIEWMARGIN &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //front
        glTranslatef(frontOffsetX,-frontOffsetY,0);
        gluLookAt(frontR*cos(0.0)*cos(0.0), frontR*sin(0.0), frontR*sin(0.0)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (x>=halfWidth+CELL_FOURVIEWMARGIN && x<=windowWidth &&
               y>=halfHeight+CELL_FOURVIEWMARGIN && y<=windowHeight) {
        //right
        glTranslatef(rightOffsetX,-rightOffsetY,0);
        gluLookAt(rightR*cos(pi/2)*cos(0.0), rightR*sin(0.0), rightR*sin(pi/2)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    drawGlandular();

    glFlush();
    hits = glRenderMode(GL_RENDER);
    if (keyValue == CELL_KEYRELEASE)
        processHits(hits, selectBuf, 0);
    else if (keyValue == CELL_SHIFT)
        processHits(hits, selectBuf, 6);
    else
        processHits(hits, selectBuf, 0);

    resizeGL(windowWidth, windowHeight);
}


void GLWidget::pickTranslateAxis(int x, int y)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(BUFSIZE, selectBuf);
    (void) glRenderMode(GL_SELECT);	/* Enter select mode */
    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble) x,
                  (GLdouble) (viewport[3] - y),
                  10.0, 10.0,
                  viewport
                  );
    if (viewMode == CELL_PERSPECTIVE) {
        gluPerspective(45,(double)windowWidth/(double)windowHeight,0.01,1000);
    } else if (viewMode == CELL_TOP) {
        glOrtho(-topR*sin(pi/8), topR*sin(pi/8),
                -topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_FRONT) {
        glOrtho(-frontR*sin(pi/8), frontR*sin(pi/8),
                -frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_RIGHT) {
        glOrtho(-rightR*sin(pi/8), rightR*sin(pi/8),
                -rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (viewMode == CELL_PERSPECTIVE) {
        glTranslatef(offsetX/R*2,-offsetY/R*2,0);
        gluLookAt(R*cos(Phi)*cos(Theta), R*sin(Theta), R*sin(Phi)*cos(Theta),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_TOP) {
        glTranslatef(topOffsetX,-topOffsetY,0);
        gluLookAt(topR*cos(0.0)*cos(pi/2), topR*sin(pi/2), topR*sin(0.0)*cos(pi/2),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_FRONT) {
        glTranslatef(frontOffsetX,-frontOffsetY,0);
        gluLookAt(frontR*cos(0.0)*cos(0.0), frontR*sin(0.0), frontR*sin(0.0)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_RIGHT) {
        glTranslatef(rightOffsetX,-rightOffsetY,0);
        gluLookAt(rightR*cos(pi/2)*cos(0.0), rightR*sin(0.0), rightR*sin(pi/2)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    drawTranslateAxis(0.0, 0.0, 0.0, selectObject);

    glFlush();
    hits = glRenderMode(GL_RENDER);
    processHits(hits, selectBuf, 1);

    resizeGL(windowWidth, windowHeight);
}

void GLWidget::pickRotateAxis(int x, int y)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(BUFSIZE, selectBuf);
    (void) glRenderMode(GL_SELECT);	/* Enter select mode */
    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble) x,
                  (GLdouble) (viewport[3] - y),
                  10.0, 10.0,
                  viewport
                  );
    if (viewMode == CELL_PERSPECTIVE) {
        gluPerspective(45,(double)windowWidth/(double)windowHeight,0.01,1000);
    } else if (viewMode == CELL_TOP) {
        glOrtho(-topR*sin(pi/8), topR*sin(pi/8),
                -topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_FRONT) {
        glOrtho(-frontR*sin(pi/8), frontR*sin(pi/8),
                -frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_RIGHT) {
        glOrtho(-rightR*sin(pi/8), rightR*sin(pi/8),
                -rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (viewMode == CELL_PERSPECTIVE) {
        glTranslatef(offsetX/R*2,-offsetY/R*2,0);
        gluLookAt(R*cos(Phi)*cos(Theta), R*sin(Theta), R*sin(Phi)*cos(Theta),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_TOP) {
        glTranslatef(topOffsetX,-topOffsetY,0);
        gluLookAt(topR*cos(0.0)*cos(pi/2), topR*sin(pi/2), topR*sin(0.0)*cos(pi/2),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_FRONT) {
        glTranslatef(frontOffsetX,-frontOffsetY,0);
        gluLookAt(frontR*cos(0.0)*cos(0.0), frontR*sin(0.0), frontR*sin(0.0)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_RIGHT) {
        glTranslatef(rightOffsetX,-rightOffsetY,0);
        gluLookAt(rightR*cos(pi/2)*cos(0.0), rightR*sin(0.0), rightR*sin(pi/2)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    drawRotateAxis(0.0, 0.0, 0.0, selectObject);

    glFlush();
    hits = glRenderMode(GL_RENDER);
    processHits(hits, selectBuf, 2);

    resizeGL(windowWidth, windowHeight);
}

void GLWidget::pickScaleAxis(int x, int y)
{
    GLuint selectBuf[BUFSIZE];
    GLint hits;
    GLint viewport[4];
    glGetIntegerv(GL_VIEWPORT, viewport);
    glSelectBuffer(BUFSIZE, selectBuf);
    (void) glRenderMode(GL_SELECT);	/* Enter select mode */
    glInitNames();
    glPushName(0);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPickMatrix((GLdouble) x,
                  (GLdouble) (viewport[3] - y),
                  10.0, 10.0,
                  viewport
                  );
    if (viewMode == CELL_PERSPECTIVE) {
        gluPerspective(45,(double)windowWidth/(double)windowHeight,0.01,1000);
    } else if (viewMode == CELL_TOP) {
        glOrtho(-topR*sin(pi/8), topR*sin(pi/8),
                -topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                topR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_FRONT) {
        glOrtho(-frontR*sin(pi/8), frontR*sin(pi/8),
                -frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                frontR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    } else if (viewMode == CELL_RIGHT) {
        glOrtho(-rightR*sin(pi/8), rightR*sin(pi/8),
                -rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,
                rightR*sin(pi/8)*(double)windowHeight/(double)windowWidth,-INT_MAX,INT_MAX);
    }

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    if (viewMode == CELL_PERSPECTIVE) {
        glTranslatef(offsetX/R*2,-offsetY/R*2,0);
        gluLookAt(R*cos(Phi)*cos(Theta), R*sin(Theta), R*sin(Phi)*cos(Theta),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_TOP) {
        glTranslatef(topOffsetX,-topOffsetY,0);
        gluLookAt(topR*cos(0.0)*cos(pi/2), topR*sin(pi/2), topR*sin(0.0)*cos(pi/2),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_FRONT) {
        glTranslatef(frontOffsetX,-frontOffsetY,0);
        gluLookAt(frontR*cos(0.0)*cos(0.0), frontR*sin(0.0), frontR*sin(0.0)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    } else if (viewMode == CELL_RIGHT) {
        glTranslatef(rightOffsetX,-rightOffsetY,0);
        gluLookAt(rightR*cos(pi/2)*cos(0.0), rightR*sin(0.0), rightR*sin(pi/2)*cos(0.0),
                  0.0,0.0,0.0, 0.0,1.0,0.0);
    }
    drawScaleAxis(0.0, 0.0, 0.0, selectObject);

    glFlush();
    hits = glRenderMode(GL_RENDER);
    processHits(hits, selectBuf, 3);

    resizeGL(windowWidth, windowHeight);
}
